#pragma once

#include <string>
#include <cstring>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <gmp.h>
#include <gmpxx.h>
#include <assert.h>
#include "json.hpp"
#include "zuc.h"
#include "sha256.h"

std::string hex2string(const char*, size_t);
void string2hex(std::string, char*, size_t);
int char2num(char);

template <typename T>
class Singleton {
 public:
  static T& Instance();
  Singleton(const Singleton&) = delete;
  Singleton& operator=(const Singleton&) = delete;
 private:
  Singleton() = default;
  ~Singleton() = default;
};
template <typename T>
T& Singleton<T>::Instance() {
  static T instance;
  return instance;
}

class PRF {
private:
    unsigned char key[32];
    int resultLength;
    SHA256 hashFunc;
    ZUC zuc;
public:
    PRF() = default;
    PRF(mpz_class k, int len): resultLength(len) {
        assert((len & 3) == 0);
        hashFunc(k.get_str());
        hashFunc.getHash(key);
        hashFunc.reset();
    }
    void setup(mpz_class k, int len) {
        assert((len & 3) == 0);
        resultLength = len;
        hashFunc(k.get_str());
        hashFunc.getHash(key);
        hashFunc.reset();
    }
    void operator() (const void *data, size_t dataLen, char* result);
    std::string operator()(std::string);
};

std::string F(std::string key, std::string m);
std::string Fp(std::string key, std::string m);
std::string H(std::string text);


std::string Enc(mpz_class key, std::string m_data);
std::string Dec(mpz_class key, std::string c_data);



